package cn.kennylee.codehub.springmvc.objectmapper.jackson;

import com.fasterxml.jackson.core.json.JsonWriteFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * <p> 扩展Jackson配置  </p>
 * <p>Created on 2024/1/27.</p>
 *
 * @author kennylee
 * @since 0.0.1
 */
@Configuration
public class JacksonConfig {

    public static final String DATETIME_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd";

    @Value("${spring.jackson.date-format:" + DATETIME_FORMAT_PATTERN + "}")
    private String datetimePattern;

    @Bean
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder objectMapperBuilder) {

        ObjectMapper objectMapper = objectMapperBuilder.createXmlMapper(false).build();

        // 注册 JavaTimeModule，以支持 Java 8 的日期和时间等API，除了刚刚build自定义过的
        objectMapper.registerModule(new JavaTimeModule());

        // 重新设置自定义Java8中特殊时间类型的序列化器和反序列化器
        resetJavaTime(objectMapper);

        // 将数字转换为字符串，解决如雪花ID的使用情景
        setupNumberAsStrings(objectMapper);
        // 设置Date类型的格式
        setupDateFormat(objectMapper);

        return objectMapper;
    }

    /**
     * 将数字转换为字符串，解决如雪花ID的使用情景
     *
     * @param objectMapper objectMapper
     */
    private static void setupNumberAsStrings(final ObjectMapper objectMapper) {
        objectMapper.configure(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS.mappedFeature(), true);
    }

    /**
     * 设置Date类型的格式
     *
     * @param objectMapper objectMapper
     */
    private static void setupDateFormat(final ObjectMapper objectMapper) {
        // 默认情况下，Jackson 会将日期转换为 Unix 时间戳（long 类型的数字），即从 1970 年 1 月 1 日以来的毫秒数
        // 例子这里使用符合人类可读字符串，所以通过将 WRITE_DATES_AS_TIMESTAMPS 设置为 false
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        // 告诉 Jackson 使用字符串表示日期格式
        objectMapper.setDateFormat(new SimpleDateFormat(DATETIME_FORMAT_PATTERN));
    }

    /**
     * 重新设置自定义Java8中特殊时间类型的序列化器和反序列化器
     *
     * @param objectMapper objectMapper
     */
    private void resetJavaTime(final ObjectMapper objectMapper) {

        final SimpleModule simpleModule = new SimpleModule();

        // 注册LocalDateTime自定义的序列化器和反序列化器
        final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(datetimePattern);
        simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
        simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));

        // 注册LocalDate自定义的序列化器和反序列化器
        simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));
        simpleModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));

        objectMapper.registerModule(simpleModule);
    }
}
